#pragma once
#include"global_var.cpp"
#include"read_atpg.cpp"
#include"gate.cpp"
#include"faults.cpp"
#include"write_result.cpp"

void clear_faults(std::vector<fault*> faults)
{
    for(auto& ptr : faults)
        delete ptr;
};

void fault_ordering(boost::unordered_flat_map<std::string,std::vector<fault*>>& unordered_faults, boost::unordered_flat_set<std::string>& visited, cell* ptr)
{
    if(visited.contains(ptr->name)) return;

    for(auto& fault : unordered_faults[ptr->name])
        collapsed_fault.emplace_back(fault);

    visited.emplace(ptr->name);
    
    for(auto& [_1,hashed_wire_name,_3,in_ptr] : ptr->input_port)
    {
        if(in_ptr!=nullptr)
            fault_ordering(unordered_faults,visited,in_ptr);
        else
        {
            std::string pi="";
            for(auto& s : sys_in)
            {
                if(hashed_wire_name != wire_hash[s]) continue;
                pi = s;
                break;
            }
            if(visited.contains(pi)) continue;
            visited.emplace(pi);
            for(auto& fault : unordered_faults[pi])
                collapsed_fault.emplace_back(fault);
        }
    }
};
void predrop_faults(std::vector<fault*>& po_faults, std::vector<fault*>& noexist_faults)
{
    for(auto& cur_fault : po_faults)
    {
        std::size_t& fe_pi_po = cur_fault->fe_pi_po;
        auto& polarity = cur_fault->polarity;
        for(int i=1,sz=vcd_outputs.begin()->second.size();i<sz;++i)
        {
            if(vcd_outputs[fe_pi_po][i] == polarity) continue;
            else if(polarity=='x') cur_fault->state = (cur_fault->state==0)?1:cur_fault->state;
            else if(polarity!='x'&&vcd_outputs[fe_pi_po][i]!='x')
            {
                cur_fault->state = 2;
                break;
            }
        }
    }
    
    for(auto& cur_fault : noexist_faults)
    {
        cur_fault->state = 0;
    }
}
void read_faults(std::string& faults_path, result& res)
{
    std::fstream faults_file(faults_path.c_str(),std::ios::in);
    std::string cmd;
    std::string gate_name;
    std::string polarity,state,location;
    fault* cur_fault = new fault;

    std::vector<fault*> po_faults;
    std::vector<fault*> noexist_faults;

    boost::unordered_flat_map<std::string,std::vector<fault*>> unordered_faults;

    getline(faults_file,cmd);
    while(1)
    {
        cur_fault->equal_faults.emplace_back(cmd);

        bool flag = !getline(faults_file,cmd);

        std::stringstream ss(cmd);
        ss>>polarity>>state;
        
        if(state!="--"||flag)
        {
            std::stringstream ss(cur_fault->equal_faults[0]);
            ss>>polarity>>state>>location;

            cur_fault->polarity = polarity.back();

            gate_name = location;

            if(gate_name.find(".")!=std::string::npos)
            {
                while(gate_name.back()!='.') gate_name.pop_back();

                std::size_t fault_port = port_hash[location.substr(gate_name.size())];

                gate_name.pop_back();

                auto& ptr = global_name_cell[gate_name];
                cur_fault->name = ptr->name;
                cur_fault->fe_port = fault_port;   

                int pos = 0;
                for(;pos<ptr->output_port.size()&&std::get<0>(ptr->output_port[pos])!=fault_port;++pos) ;

                if(pos == ptr->output_port.size())
                {
                    cur_fault->is_input = 1;
                    unordered_faults[cur_fault->name].emplace_back(cur_fault);
                }
                else if(hash_output.contains(std::get<1>(ptr->output_port[pos])))
                {
                    cur_fault->fe_pi_po = std::get<1>(ptr->output_port[pos]);
                    po_faults.emplace_back(cur_fault);
                }
                else if(std::get<1>(ptr->output_port[pos])!=0)
                    unordered_faults[cur_fault->name].emplace_back(cur_fault);
                else
                    noexist_faults.emplace_back(cur_fault);

            }
            else if(hash_input.contains(wire_hash[location]))
            {
                cur_fault->fe_pi_po = wire_hash[location];
                cur_fault->name = location;
                cur_fault->is_input = 1;
                unordered_faults[cur_fault->name].emplace_back(cur_fault);
            }
            else    
            {
                cur_fault->fe_pi_po = wire_hash[location];
                po_faults.emplace_back(cur_fault);
            }

            cur_fault = new fault;
        }

        if(flag) break;

    }
    faults_file.close();

    boost::unordered_flat_set<std::string> visited;

    for(auto& po : sys_out)
        fault_ordering(unordered_faults,visited,global_ckt[wire_hash[po]]);
    predrop_faults(po_faults, noexist_faults);
    
    res.predrop_write(po_faults);
    res.predrop_write(noexist_faults);
}
